#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__all__ = [
           u'Vector',
           u'StrList',
           u'PyLotList',
           u'Coordinate'
           ]

import ast
import cStringIO
import copy

from traits.api import implements

from _pylot.data.fileformat import DialectPyLotList, DialectXml

from _pylot.minimal.utils import (list_to_unicode_list, check_ascii, error)
from _pylot.minimal.system import ENCODING
from _pylot.minimal.csvtools import UnicodeWriter, unicode_csv_reader
from _pylot.minimal.pylotdict import PyLotDict
from _pylot.minimal.exception import CustomException
from _pylot.minimal.interfaces.i_datumtype import INature
from _pylot.minimal.xmltools import etree

class ErrNoUnicodeConstructor(CustomException):
  pass


class BaseList (list):

  implements(INature)
  identifier = 'pyLot.datumtype.BaseList'

  def __init__(self, value=[]):
    if isinstance(value, unicode):
      value = value.strip(u'()[]').split(u',')
    new_list = self.convertList(value)
    list.__init__(self, new_list)

  def __repr__ (self):
    return '%s(%s)' % (self.__class__.__name__, list.__repr__(self))

  def __str__ (self):
    return list.__str__(self)

  def __getslice__(self, b, c):
    return self.__class__(list.__getslice__(self, b, c))

  def __copy__(self):
    return self.__class__([copy.copy(val) for val in self])

  def __deepcopy__(self):
    return self.__class__([copy.copy(val) for val in self])

  def __unicode__ (self):
    output = cStringIO.StringIO()
    writer = UnicodeWriter(output, dialect=DialectXml())
    writer.writerow(list_to_unicode_list(list(self)))
    csvstr = output.getvalue().decode(u'utf-8')
    output.close()
    return u'[%s]' % csvstr.strip()

  @classmethod
  def convertList (cls, src):
    new_list = []
    for val in src :
      new_list.append(cls.convertValue(val))
    return new_list

  @classmethod
  def convertValue(cls, src):
    return src

  @classmethod
  def FromCsv (cls, src):
    src = src.strip()
    src = src.rstrip(u']')
    src = src.lstrip(u'[')

    lst_reader = unicode_csv_reader([src], dialect=DialectPyLotList())
    for row in lst_reader :
      csvlst = [field.strip() for field in row]
    csvlst = cls.convertList(csvlst)
    return cls(csvlst)

  @classmethod
  def FromPyLot (cls, src):
    return cls(src.split(u','))

  @classmethod
  def FromXmlElement (cls, node):
    src = node.text
    if src is None :
      return None
    src = unicode(src).strip()
    lst_reader = unicode_csv_reader([src], dialect=DialectXml())
    for row in lst_reader :
      csvlst = [field.strip() for field in row]
    try :
      csvlst = cls.convertList(csvlst)
    except ValueError :
      return None
    else :
      return cls(csvlst)

  def copy(self):
    return self.__copy__()

  def toPyLot (self):
    lst = list_to_unicode_list(self)
    for val in lst :
      check_ascii(u'value', val)
    return u','.join(lst)

  def toCsv (self):
    output = cStringIO.StringIO()
    writer = UnicodeWriter(output, dialect=DialectPyLotList())
    writer.writerow(list_to_unicode_list(list(self)))
    csvstr = output.getvalue().decode(u'utf-8')
    output.close()
    return u'[%s]' % csvstr.strip()

  def toXmlElement(self, role=None):
    node = etree.Element(u'value')
    if role :
      node.attrib[u'role'] = role
    node.text = unicode(self)
    return node

class StrList (BaseList):
  u"""
  StrList provides a list containing only unicode strings.
  """

  @classmethod
  def convertValue(cls, val):
    if isinstance(val, str) :
      return val.decode(ENCODING)
    else :
      return unicode(val)

class Vector (BaseList):
  u"""
  Vector provides a list containing only float values
  """
  identifier = 'pyLot.datumtype.Vector'

  def __repr__ (self):
    value_list = u', '.join(list_to_unicode_list(self))
    return '%s([%s])' % (self.__class__.__name__, value_list)

  def __str__ (self):
    value_list = u', '.join(list_to_unicode_list(self))
    return '(%s)' % value_list

  def __unicode__ (self):
    value_list = u', '.join(list_to_unicode_list(self))
    return '(%s)' % value_list

  @classmethod
  def convertValue(cls, val):
    if val == 0 :
      return 0.
    else :
      return float(val)

  @classmethod
  def FromCsv (cls, src):
    src = src.strip()
    return cls(ast.literal_eval(src))

class Coordinate(Vector):
  u"""
  Coordinates provides a list containing exactly 3 float values.

  >>> Coordinate([1,2,3])
  Coordinate([1.0, 2.0, 3.0])
  >>> Coordinate(u'(1,2,3)')
  Coordinate([1.0, 2.0, 3.0])
  >>> Coordinate(u'1, 2, 3')
  Coordinate([1.0, 2.0, 3.0])
  """

  identifier = 'pyLot.datumtype.Coordinate'

  def __init__(self, value=[0, 0, 0]):
    if isinstance(value, unicode):
      value = value.strip(u'()[]').split(u',')
    new_list = self.convertList(value)
    if len(new_list) != 3 :
      raise ValueError, 'Coordinate must have exactly 3 components'
    list.__init__(self, new_list)

  def _toDict(self, prefix=u'', coord_names=[u'x', u'y', u'z'], dic=dict):
    dic = dic()
    for i, coord in enumerate(coord_names):
      dic[prefix + coord] = self[i]
    return dic

  def toDict(self, prefix=u'', coord_names=[u'x', u'y', u'z']):
    """
    >>> pt = Coordinate()
    >>> keys = pt.toDict().keys()
    >>> keys.sort()
    >>> keys
    [u'x', u'y', u'z']

    >>> keys = pt.toDict(u'velocity_').keys()
    >>> keys.sort()
    >>> keys
    [u'velocity_x', u'velocity_y', u'velocity_z']

    :param dic: class used to generate dict. Default: builtin dict
    """
    return self._toDict(prefix, coord_names)

  def toPyLotDict(self, prefix=u'', coord_names=[u'x', u'y', u'z']):
    return self._toDict(prefix=prefix, coord_names=coord_names, dic=PyLotDict)

  def toCgnsDict(self):
    return self._toDict(prefix=u'Coordinate', coord_names=[u'X', u'Y', u'Z'])

  def toCgnsPyLotDict(self):
    return self._toDict(prefix=u'Coordinate', coord_names=[u'X', u'Y', u'Z'],
                        dic=PyLotDict)

  def toList(self):
    return [coord for coord in self]

  def norm(self):
    from math import sqrt
    return sqrt(self[0] ** 2 + self[1] ** 2 + self[2] ** 2)


class PyLotList (StrList):
  u"""
  PyLotList provides a list containing only ASCII characters
  """
  @classmethod
  def convertValue(cls, val):
    val = StrList.convertValue(val)
    check_ascii(u'value', val)
    if u',' in val :
      raise ValueError, error(tr_(u'E001 - value: character "," is forbidden in PyLotList values'))
    return val

valid_datum_types = []

if __name__ == '__main__':
  import doctest
  doctest.testmod()
